import torch
import numpy as np
import pandas as pd
import collections
import random
from sys import getsizeof, stderr

#自定义文件导入
from env import Creator, Packer, PackerStable
from sseHp import Hyperparameter
from datetime import datetime
from itertools import chain
from collections import deque
from matplotlib import pyplot as plt
try:
    from reprlib import repr
except ImportError:
    pass
import matplotlib.pyplot as plt
import numpy as np
import sseHp
#创建全局变量
creator = Creator() #Creator不用初始化
hp = Hyperparameter() #因为是类，所以需要创建实例



def calculate_rate_drl_in_detail(carlist,items, index, prob):
    i = index
    rate_list = []
    items = items.squeeze()
    num_list = []
    temp = 0
    items_list = []
    prob_list = []
    p_list = []
    income_list = []
    p = list(prob[index])
    load_items = creator.bin_create(items.clone(), i) # 装箱
    packer = Packer()
    # print(len(load_items))
    while len(load_items) > 0:
        if len(rate_list) == 0:
            # print(len(load_items))
            #carlist为list形式后续可以放长宽高不一样的车厢
            carlist[0].position = [0, 0, 0] #置空
            rate, num, item_list, peritem_list = packer.packer_own(carlist, load_items)
            temp = num
            # print(num,len(load_items))
            rate_list.append(rate)
            num_list.append(num)
            items_list.append(item_list)
            weight_list = []
            for i in item_list:
                weight_list.append(i.weight)
            income_list.append(weight_list)
            for k in range(num):
                prob_list.append(p[k])
            for k in range(num):
                p.pop(0)
        else:
            carlist[0].position = [0, 0, 0] #置空
            rate, num, item_list, peritem_list = packer.packer_own(carlist, load_items)
            rate_list.append(rate)
            num_list.append(num)
            items_list.append(item_list)
            weight_list = []
            for i in item_list:
                weight_list.append(i.weight)
            income_list.append(weight_list)
            temp = num
            for k in range(num):
                prob_list.append(p[k])
            for k in range(num):
                p.pop(0)
        p_list.append(prob_list)
        prob_list = []
    try:
        avg_rate = torch.sum(torch.tensor(rate_list))/len(rate_list)
    except:
        raise ZeroDivisionError
    return avg_rate, items_list, p_list, num_list, rate_list, income_list

#
def calculate_rate_drl_in_detail_qoilieb(carlist,items, index, prob):
    i = index
    rate_list = []
    items = items.squeeze()
    num_list = []
    temp = 0
    items_list = []
    prob_list = []
    p_list = []
    income_list = []
    families_list = []
    p = list(prob[index])
    load_items = creator.bin_create(items.clone(), i) # 装箱
    packer = Packer()
    # print(len(load_items))
    while len(load_items) > 0:
        if len(rate_list) == 0:
            # print(len(load_items))
            #carlist为list形式后续可以放长宽高不一样的车厢
            carlist[0].position = [0, 0, 0] #置空
            rate, num, item_list, family_list = packer.packer_family(carlist, load_items)
            temp = num
            # print(num,len(load_items))
            rate_list.append(rate)
            num_list.append(num)
            items_list.append(item_list)
            families_list.append(family_list)
            weight_list = []
            for i in item_list:
                weight_list.append(i.weight)
            income_list.append(weight_list)
            for k in range(num):
                prob_list.append(p[k])
            for k in range(num):
                p.pop(0)
        else:
            carlist[0].position = [0, 0, 0] #置空
            rate, num, item_list, family_list = packer.packer_family(carlist, load_items)
            rate_list.append(rate)
            num_list.append(num)
            items_list.append(item_list)
            families_list.append(family_list)
            weight_list = []
            for i in item_list:
                weight_list.append(i.weight)
            income_list.append(weight_list)
            temp = num
            for k in range(num):
                prob_list.append(p[k])
            for k in range(num):
                p.pop(0)
        p_list.append(prob_list)
        prob_list = []
    try:
        avg_rate = torch.sum(torch.tensor(rate_list))/len(rate_list)
    except:
        raise ZeroDivisionError
    return avg_rate, items_list, p_list, rate_list, income_list, families_list


def calculate_rate_drl_in_detail_family(carlist, items, index, prob):
    i = index
    rate_list = []
    items = items.squeeze()
    num_list = []
    temp = 0
    items_list = []
    prob_list = []
    p_list = []
    income_list = []
    families_list = []
    p = list(prob[index])
    load_items = creator.bin_create_num(items.clone(), i) # 装箱
    packer = Packer()
    # print(len(load_items))
    while len(load_items) > 0:
        if len(rate_list) == 0:
            # print(len(load_items))
            #carlist为list形式后续可以放长宽高不一样的车厢
            carlist[0].position = [0, 0, 0] #置空
            rate, num, item_list, family_list = packer.packer_family(carlist, load_items)
            temp = num
            # print(num,len(load_items))
            rate_list.append(rate)
            num_list.append(num)
            items_list.append(item_list)
            families_list.append(family_list)
            weight_list = []
            for i in item_list:
                weight_list.append(i.weight)
            income_list.append(weight_list)
            for k in range(num):
                prob_list.append(p[k])
            for k in range(num):
                p.pop(0)
        else:
            carlist[0].position = [0, 0, 0] #置空
            rate, num, item_list, family_list = packer.packer_family(carlist, load_items)
            rate_list.append(rate)
            num_list.append(num)
            items_list.append(item_list)
            families_list.append(family_list)
            weight_list = []
            for i in item_list:
                weight_list.append(i.weight)
            income_list.append(weight_list)
            temp = num
            for k in range(num):
                prob_list.append(p[k])
            for k in range(num):
                p.pop(0)
        p_list.append(prob_list)
        prob_list = []
    try:
        avg_rate = torch.sum(torch.tensor(rate_list))/len(rate_list)
    except:
        raise ZeroDivisionError
    # 统计family情况
    fl_sum_list = []
    for i in families_list:
        fl_unit = [0, 0, 0, 0]
        for j in i:
            fl_unit[int(j) - 1] += 1
        fl_sum_list.append(fl_unit)
    return avg_rate, items_list, p_list, rate_list, income_list, fl_sum_list

def calculate_rate_drl_in_detail_family_stable(carlist,items, index, prob):
    #稳定装载货物
    i = index
    rate_list = []
    items = items.squeeze()
    num_list = []
    temp = 0
    items_list = []
    prob_list = []
    p_list = []
    income_list = []
    families_list = []
    p = list(prob[index])
    load_items = creator.bin_create_num(items.clone(), i) # 装箱
    packer = PackerStable()
    # print(len(load_items))
    while len(load_items) > 0:
        if len(rate_list) == 0:
            # print(len(load_items))
            #carlist为list形式后续可以放长宽高不一样的车厢
            carlist[0].position = [0, 0, 0] #置空
            rate, num, item_list, family_list = packer.packer_family_stable_2(carlist, load_items)
            print("装载了一个货箱！！")
            temp = num
            # print(num,len(load_items))
            rate_list.append(rate)
            num_list.append(num)
            items_list.append(item_list)
            families_list.append(family_list)
            weight_list = []
            for i in item_list:
                weight_list.append(i.weight)
            income_list.append(weight_list)
            for k in range(num):
                prob_list.append(p[k])
            for k in range(num):
                p.pop(0)
        else:
            carlist[0].position = [0, 0, 0] #置空
            rate, num, item_list, family_list = packer.packer_family_stable_2(carlist, load_items)
            #print("装载了一个货箱！！")
            rate_list.append(rate)
            num_list.append(num)
            items_list.append(item_list)
            families_list.append(family_list)
            weight_list = []
            for i in item_list:
                weight_list.append(i.weight)
            income_list.append(weight_list)
            temp = num
            for k in range(num):
                prob_list.append(p[k])
            for k in range(num):
                p.pop(0)
        p_list.append(prob_list)
        prob_list = []
    try:
        avg_rate = torch.sum(torch.tensor(rate_list))/len(rate_list)
    except:
        raise ZeroDivisionError
    # 统计family情况
    fl_sum_list = []
    for i in families_list:
        fl_unit = [0, 0, 0, 0]
        for j in i:
            fl_unit[int(j) - 1] += 1
        fl_sum_list.append(fl_unit)
    return avg_rate, items_list, p_list, rate_list, income_list, fl_sum_list

def calculate_rate_drl_in_detail_family_buyicixu(carlist,items, index, prob):
    i = index
    rate_list = []
    items = items.squeeze()
    num_list = []
    temp = 0
    items_list = []
    prob_list = []
    p_list = []
    income_list = []
    families_list = []
    p = list(prob[index])
    load_items = creator.bin_create(items.clone(), i) # 装箱
    packer = Packer()
    # print(len(load_items))
    while len(load_items) > 0:
        if len(rate_list) == 0:
            # print(len(load_items))
            #carlist为list形式后续可以放长宽高不一样的车厢
            carlist[0].position = [0, 0, 0] #置空
            rate, num, item_list, family_list = packer.packer_family(carlist, load_items)
            temp = num
            # print(num,len(load_items))
            rate_list.append(rate)
            num_list.append(num)
            items_list.append(item_list)
            families_list.append(family_list)
            weight_list = []
            for i in item_list:
                weight_list.append(i.weight)
            income_list.append(weight_list)
            for k in range(num):
                prob_list.append(p[k])
            for k in range(num):
                p.pop(0)
        else:
            carlist[0].position = [0, 0, 0] #置空
            rate, num, item_list, family_list = packer.packer_family(carlist, load_items)
            rate_list.append(rate)
            num_list.append(num)
            items_list.append(item_list)
            families_list.append(family_list)
            weight_list = []
            for i in item_list:
                weight_list.append(i.weight)
            income_list.append(weight_list)
            temp = num
            for k in range(num):
                prob_list.append(p[k])
            for k in range(num):
                p.pop(0)
        p_list.append(prob_list)
        prob_list = []
    try:
        avg_rate = torch.sum(torch.tensor(rate_list))/len(rate_list)
    except:
        raise ZeroDivisionError
    # 统计family情况
    fl_sum_list = []
    for i in families_list:
        fl_unit = [0, 0, 0, 0]
        for j in i:
            fl_unit[int(j) - 1] += 1
        fl_sum_list.append(fl_unit)
    return avg_rate, items_list, p_list, rate_list, income_list, fl_sum_list
#序列分析专用
def calculate_rate_drl_in_detail_family_orderAnalysis(carlist, items, index):
    i = index
    rate_list = []
    items = items.squeeze()
    num_list = []
    temp = 0
    items_list = []
    prob_list = []
    income_list = []
    families_list = []
    load_items = creator.bin_create_num(items.clone(), i) # 装箱
    packer = Packer()
    # print(len(load_items))
    while len(load_items) > 0:
        if len(rate_list) == 0:
            # print(len(load_items))
            #carlist为list形式后续可以放长宽高不一样的车厢
            carlist[0].position = [0, 0, 0] #置空
            rate, num, item_list, family_list = packer.packer_family(carlist, load_items)
            temp = num
            # print(num,len(load_items))
            rate_list.append(rate)
            num_list.append(num)
            items_list.append(item_list)
            families_list.append(family_list)
            weight_list = []
            for i in item_list:
                weight_list.append(i.weight)
            income_list.append(weight_list)
        else:
            carlist[0].position = [0, 0, 0] #置空
            rate, num, item_list, family_list = packer.packer_family(carlist, load_items)
            rate_list.append(rate)
            num_list.append(num)
            items_list.append(item_list)
            families_list.append(family_list)
            weight_list = []
            for i in item_list:
                weight_list.append(i.weight)
            income_list.append(weight_list)
            temp = num
    try:
        avg_rate = torch.sum(torch.tensor(rate_list))/len(rate_list)
    except:
        raise ZeroDivisionError
    # 统计family情况
    fl_sum_list = []
    for i in families_list:
        fl_unit = [0, 0, 0, 0]
        for j in i:
            fl_unit[int(j) - 1] += 1
        fl_sum_list.append(fl_unit)
    return avg_rate, items_list, rate_list, income_list, fl_sum_list

def ils2value(ilz,carlist):
    listsa = []
    for i in ilz:
        vol = 0
        vol_list = []
        for j in i:
            vol_list.append(j.volume / carlist[0].volume)
        listsa.append(vol_list)
    return listsa
#单位收益除以总体收益
def pl2rate(il):
    res_list = []
    res_sum = 0
    for i in il:
        pl = 0
        for j in i:
            pl += j
        res_list.append(pl)
        res_sum += pl
    avg_pl_list = []
    for i in il:
        avg_pl = []
        for j in i:
            avg_pl.append(j/res_sum)
        avg_pl_list.append(avg_pl)
    return avg_pl_list, 1. / len(il)

#单位收益除以每个货箱的收益
def pl2rate_UnitIncomeDevidePerBinIncome(il):
    res_list = []
    res_sum = 0
    for i in il:
        pl = 0
        for j in i:
            pl += j
        res_list.append(pl)
        res_sum += pl
    avg_pl_list = []
    incomeRate_perbin_list = []
    for i in range(len(il)):
        avg_pl = []
        sum_perbin = 0
        for j in il[i]:
            avg_pl.append(j / res_list[i])
            sum_perbin += j
        incomeRate_perbin_list.append(sum_perbin/res_sum) #
        avg_pl_list.append(avg_pl)
    wl_tensor_sum = torch.tensor(incomeRate_perbin_list)

    mean_ = 1./len(wl_tensor_sum)
    #mean_ = torch.mean(wl_tensor_sum)
    sse = 0
    for i in wl_tensor_sum:
        sse += (i-mean_)*(i-mean_)/len(wl_tensor_sum)#torch.abs(i-mean_)
    return avg_pl_list, sse, incomeRate_perbin_list

def get_msse_reward(il):
    M = len(il)
    MAX_J = 0
    for k in il:
        len_ = len(k)
        if len_ > MAX_J:
            MAX_J = len_
    rewards_2dim_0 = torch.zeros((M, MAX_J))
    #补零操作
    for k in range(M):
        for j in range(len(il[k])):
            rewards_2dim_0[k][j] = il[k][j]
    #奖励计算
    rewards_2dim = torch.zeros((M, MAX_J))
    for j in range(MAX_J):
        sum_ = 0
        for kk in range(M):
            sum_ += rewards_2dim_0[kk][j]
        avg_ = sum_ / M
        for kk in range(M):
            rewards_2dim[kk, j] = (rewards_2dim_0[kk][j] - avg_) * (rewards_2dim_0[kk][j] - avg_) * (-1)
    reward_list = []
    for k in range(M):
        reward_list_ = []
        for j in range(len(il[k])):
            reward_list_.append(rewards_2dim[k][j].item())
        reward_list.append(reward_list_)
    return reward_list

def linear_reward(rs_list, weights_list, a1, a2, Gamma):
    #max_asu = np.min(np.min(np.array(rs_list)))
    #min_asu = np.max(np.max(np.array(rs_list)))
    #max_gini = np.min(np.min(np.array(weights_list)))  # 实验结果经验所得，可调参数
    #min_gini = np.max(np.max(np.array(weights_list)))  # 实验结果经验所得，可调参数
    len_weights_list = len(weights_list)
    reward_linear = []
    for i in range(len_weights_list):
        reward_topsis_iner = []
        for j in range(len(rs_list[i])):
            asu_asu_normalization = rs_list[i][j]#(rs_list[i][j] - min_asu) / torch.sigmoid(torch.tensor(max_asu - min_asu)) #rs_list[i][j]#
            asu_gini_normalization = weights_list[i][j]#(max_gini - weights_list[i][j]) / torch.sigmoid(torch.tensor(max_gini - min_gini)) #weights_list[i][j]#
            reward_topsis_iner.append(a1 * asu_asu_normalization + a2 * asu_gini_normalization)
        reward_linear.append(reward_topsis_iner)
    dis_list = []
    for i in reward_linear:  # reward_topsis 原 rs_list
        discounted_ep_rs = np.zeros_like(i)
        running_add = 0
        for t in reversed(range(0, len(i))):
            running_add = running_add * Gamma + i[t]
            discounted_ep_rs[t] = running_add
        discounted_ep_rs = discounted_ep_rs - np.mean(discounted_ep_rs)  # 减均值
        discounted_ep_rs = discounted_ep_rs / np.std(discounted_ep_rs)  # 除以标准差 当或向只有一个货物时，标准差是0
        discounted_ep_rs = torch.FloatTensor(discounted_ep_rs)
        dis_list.append(discounted_ep_rs)
    return reward_linear, dis_list


def linear_reward_newReward(rs_list, incomeRate_list, a1, a2, Gamma):
    M = len(rs_list)

    max_k_len = 0
    min_k_len = 99999
    for k in rs_list:
        len_ = len(k)
        if len_ > max_k_len:
            max_k_len = len_
        if len_ < min_k_len:
            min_k_len = len_
    # 处理装载数量并不相同的货箱数据，变为 一样长，不足的部分补0
    rs_list_0 = torch.zeros((M, max_k_len))
    incomeRate_list_0 = torch.zeros((M, max_k_len))
    for k in range(M):
        for j in range(len(rs_list[k])):
            rs_list_0[k][j] = rs_list[k][j]
            incomeRate_list_0[k][j] = incomeRate_list[k][j]
    rs_1dim = [] #一维化
    income_1dim = [] #一维化
    for k in range(M):
        _len_ = len(rs_list[k])
        for j in range(_len_):
            rs_1dim.append(rs_list[k][j])
            income_1dim.append(incomeRate_list[k][j])
    rs_1dim_tensor = torch.tensor(rs_1dim)
    income_1dim_tensor = torch.tensor(income_1dim)
    #零均值归一化
    rs_mean = torch.mean(rs_1dim_tensor)
    rs_std = torch.std(rs_1dim_tensor)
    income_mean = torch.mean(income_1dim_tensor)
    income_std = torch.std(income_1dim_tensor)

    rs_standard = torch.zeros((M, max_k_len))
    income_standard = torch.zeros((M, max_k_len))
    for j in range(max_k_len):
        for k in range(M):
            rs_standard[k][j] = (rs_list_0[k, j] - rs_mean) / rs_std
            income_standard[k][j] = (incomeRate_list_0[k, j] - income_mean ) / income_std
    reward_list_0 = []
    for j in range(max_k_len):
        list_ = []
        for k in range(M):
            list_.append(a1 * rs_standard[k, j] + a2 * income_standard[k, j])
        reward_list_0.append(msse_multiple_variables(list_))
    sum__ = np.sum(np.array(reward_list_0))
    reward_list = []

    for k in range(M):
        _len_ = len(rs_list[k])
        for j in range(_len_):
            reward_list.append(reward_list_0[j])
    rewards = torch.tensor(reward_list)
    # rewards标准化
    rewards_sum = torch.sum(rewards)
    rewards_stand = rewards/rewards_sum


    #dis_list = []
    dis_list = torch.zeros_like(rewards)
    running_add = 0
    for i in reversed(range(0, len(rewards_stand))):  # reward_topsis 原 rs_list
        running_add = running_add * Gamma + rewards_stand[i]  # *(1/len(rs_list))
        dis_list[i] = running_add
        #discounted_ep_rs = discounted_ep_rs - np.mean(discounted_ep_rs)  # 减均值
        #discounted_ep_rs = discounted_ep_rs / np.std(discounted_ep_rs)  # 除以标准差
    dis_list = torch.FloatTensor(dis_list)
    return rewards, dis_list



def Z_ScoreNormalization(x, mu, sigma):
    x = (x - mu) / sigma
    return x
def msse_multiple_variables(variables):
    sum_ = 0
    for i in variables:
        sum_ += (i - np.mean(variables)) * (i - np.mean(variables))
    return sum_/len(variables)


def getRewardTheSizeEqualPro(ils, reward_, dis_list_):
    a = []
    for i in ils:
        for j in i:
            a.append(int(j.name))
    b = []
    for i in reward_:
        for j in i:
            b.append(j)
    c = []
    for i in dis_list_:
        for j in i:
            c.append(j)
    d = torch.zeros((len(a)))
    for i in range(len(a)):
        d[a[i]] = b[i]
    e = torch.zeros((len(a)))
    for i in range(len(a)):
        e[a[i]] = c[i]
    return d, e

def topsis_reward(rs_list,weights_list,Gamma):
    # 参数设定
   # max_asu = np.min(np.min(np.array(rs_list)))
    #min_asu = np.max(np.max(np.array(rs_list)))
   # max_gini = np.min(np.min(np.array(weights_list)))  # 实验结果经验所得，可调参数
    #min_gini = np.max(np.max(np.array(weights_list)))  # 实验结果经验所得，可调参数
    len_weights_list = len(weights_list)
    reward_topsis = []
    for i in range(len_weights_list):
        reward_topsis_iner = []
        for j in range(len(rs_list[i])):
            asu_asu_normalization =rs_list[i][j]# (rs_list[i][j] - min_asu) / (max_asu - min_asu)
            asu_gini_normalization = weights_list[i][j]#(max_gini - weights_list[i][j]) / (max_gini - min_gini)
            asu_D_max = torch.sqrt(torch.pow(asu_asu_normalization - 1, 2) + torch.pow(asu_gini_normalization, 2))
            asu_D_min = torch.sqrt(torch.pow(asu_asu_normalization, 2) + torch.pow(asu_gini_normalization - 1, 2))
            # 计算多目标的总奖励，距离最劣值越远越好，也就是这个比值越大越好
            reward_topsis_iner.append(asu_D_min / (asu_D_max + asu_D_min))
        reward_topsis.append(reward_topsis_iner)
    dis_list = []
    for i in reward_topsis:  # reward_topsis 原 rs_list
        discounted_ep_rs = np.zeros_like(i)
        running_add = 0
        for t in reversed(range(0, len(i))):
            running_add = running_add * Gamma + i[t]  # *(1/len(rs_list))
            discounted_ep_rs[t] = running_add
        discounted_ep_rs = discounted_ep_rs - np.mean(discounted_ep_rs)  # 减均值
        discounted_ep_rs = discounted_ep_rs / np.std(discounted_ep_rs)  # 除以标准差
        discounted_ep_rs = torch.FloatTensor(discounted_ep_rs)
        dis_list.append(discounted_ep_rs)
    return reward_topsis, dis_list
def capulted_topsis_pingfen(avg_rate_asu, gini_asu):
    # 参数设定
    max_asu = 0.70
    min_asu = 0.40
    max_sse = 0.02  # 实验结果经验所得，可调参数
    min_sse = 0.00  # 实验结果经验所得，可调参数
    # 成本值：gini效益值：asu
    asu_asu_normalization = (avg_rate_asu - min_asu) / (max_asu - min_asu)
    asu_sse_normalization = (max_sse - gini_asu) / (max_sse - min_sse)
    # 计算与最优值的距离
    asu_D_max = torch.sqrt(torch.pow(asu_asu_normalization - 1, 2) + torch.pow(asu_sse_normalization, 2))
    asu_D_min = torch.sqrt(torch.pow(asu_asu_normalization, 2) + torch.pow(asu_sse_normalization - 1, 2))
    # 计算多目标的总奖励，距离最劣值越远越好，也就是这个比值越小越好
    reward_asus_topsis = asu_D_min / (asu_D_max + asu_D_min)
    return asu_D_max, asu_D_min, reward_asus_topsis

def capulted_topsis_avgRandavgPL(avg_rate_asu, sse_asu):
    # 参数设定

    # 成本值：gini效益值：asu
    asu_normalization = avg_rate_asu
    sse_normalization = sse_asu
    # 计算与最优值的距离
    asu_D_max = torch.sqrt(torch.pow(asu_normalization - 1, 2) + torch.pow(sse_normalization, 2))
    asu_D_min = torch.sqrt(torch.pow(asu_normalization, 2) + torch.pow(sse_normalization - 1, 2))
    # 计算多目标的总奖励，距离最劣值越远越好，也就是这个比值越大越好
    reward_asus_topsis = asu_D_min / (asu_D_max + asu_D_min)
    return reward_asus_topsis
def sse_computer(wls):
    all_sum = 0
    for i in wls:
        for j in i:
            all_sum += j
    weights_list_binsnumber = []
    for i in wls:
        sum_w = 0
        for j in i:
            sum_w = sum_w + j
        weights_list_binsnumber.append(sum_w/all_sum)
    wl_tensor_sum = torch.tensor(weights_list_binsnumber)
    mean_ = 1./len(wl_tensor_sum)
    sse = 0
    for i in wl_tensor_sum:
        sse += (i-mean_)*(i-mean_)/len(wl_tensor_sum)
    return sse


def save_network(agent, train_items, items, q_episode, buffer, car_list):

    asu_pro, asu_ind = agent.network.forward(train_items)  ####需要将四个值降下来一个，变成1200,1,3
    avg_rate_asu, items_list_asu, p_list_asu, _, rate_list_asu, weights_list_asu = calculate_rate_drl_in_detail(
        car_list, items, asu_ind, asu_pro)
    # 按照序列重排序
    # 对象转换成长宽高的Tensor。
    # items_overall_asu_ = torch.tensor(torch.gather(torch.tensor(items_list_size_asu),dim = 0,index = asu_ind))
    # items_overall_gini = torch.tensor(items_list_gini)
    # buffer state对应items_list: 不同货箱按照序列装载货物后的货物集合。 action对应ind: 序列，3,5,6,8,9... 。rate_list对应空间利用率的列表集合，二维。items_overall：所有货物的集合
    # 将rate_list换成与最优值的的距离（距离用TOPSIS方法计算）
    # gini_asu = bpUtils.gini_computer(weights_list_asu)
    # 将MSE作为评分标准
    gini_asu = sse_computer(weights_list_asu)
    # **************************** 计算距离：非动态变化的最大小值 ***************************
    # 计算多目标的总奖励，距离最劣值越远越好，也就是这个比值越大越好
    reward_asus_topsis = capulted_topsis_pingfen(avg_rate_asu, gini_asu)
    # reward_asus_topsis = bpUtils.capulted_mse_pingfen(avg_rate_asu, gini_asu)
    # **************************** 计算距离完 ***************************
    net_name_1 = "net/net_" + str(q_episode) + "_1.pt"
    agent.save_netModel(net_name_1)
    # 将最优解与最劣值的距离奖励值放入缓冲池，
    buffer.add_1(net_name_1, reward_asus_topsis)
    return None
def view_packing_origin():
    def randomcolor():
        colorArr = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
        color = ""
        for i in range(6):
            color += colorArr[random.randint(0, 14)]
        return "#" + color

    # 明确坐标范围
    x, y, z = np.indices((10, 8, 8))

    # 设置3个长方体x、y、z的范围
    cube1 = (x <= 0) & (y <= 4) & (z <= 3)
    cube2 = (x <= 3) & (y >= 4) & (z <= 3)
    cube3 = (x >= 0) & (x <= 2) & (y >= 1) & (y <= 2) & (z >= 3) & (z <= 6)
    cube4 = (x >= 3) & (x <= 5) & (y >= 1) & (y <= 2) & (z >= 3) & (z <= 6)
    cube5 = (x >= 6) & (x <= 9) & (y >= 1) & (y <= 2) & (z >= 3) & (z <= 6)

    cubes = [cube1, cube2, cube3,cube4,cube5]
    # 绘图
    fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
    for i in range(len(cubes)):
        ax.voxels(cubes[i], edgecolor=randomcolor())
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')
    plt.show()

def view_packing(car, items,No_):
    # def randomcolor():
    #
    #     colorArr = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
    #     color = ""
    #     for i in range(6):
    #         color += colorArr[random.randint(0, 14)]
    #     return "#" + color
    #改进：四类货箱只用四类颜色，并固定好颜色库
    colors_rep = ["#F3BCC2", "#FCE3BD", "#C2E1C4", "#BAE1E8"]


    packer = Packer()

    # 明确坐标范围
    _,_,_,cubes = packer.packer_oneCar_cube(car, items)
    # 绘图
    fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
    for i in cubes:
        ax.voxels(i, edgecolor=colors_rep)
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')
    strnow = datetime.strftime(datetime.now(), '%Y%m%d%H%M%S')
    file_name = str(strnow) +"_NO."+str(No_)+"Bin"+".csv"
    #plt.show()
    fig.savefig(fname=r'F:\my_background\SSETest\PltTest\mycode\fig\r'+file_name+'.jpg')
def getColorByClass(family_number):
    colors_rep = ["#F3BCC2", "#FCE3BD", "#C2E1C4", "#BAE1E8"]
    return colors_rep[int(family_number-1)]
def view_packing_family(car, items, No_):
    # def randomcolor():
    #
    #     colorArr = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
    #     color = ""
    #     for i in range(6):
    #         color += colorArr[random.randint(0, 14)]
    #     return "#" + color
    #改进：四类货箱只用四类颜色，并固定好颜色库

    packer = Packer()

    # 明确坐标范围
    _, _, il, cubes, family = packer.packer_oneCar_cube_family(car, items)
    #验证货物集合的族类与族类集合的对应位置相等
    count = 0
    for i in range(len(il)):
        if il[i].family == family[i]:
            count += 1
    if count == len(il):
        print("装在的情况货物与族类相对应：", True)

    # 绘图
    fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
    for i in range(len(cubes)):
        color_ = getColorByClass(family[i])
        ax.voxels(cubes[i], edgecolor=color_)
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')
    strnow = datetime.strftime(datetime.now(), '%Y%m%d%H%M%S')
    file_name = str(strnow) +"_NO."+str(No_)+"Bin"+".csv"
    #plt.show()
    fig.savefig(fname=r'F:\my_background\SSETest\PltTest\mycode\fig\r'+file_name+'.jpg')


def draw_first_car_boxs(carlist,items, index, prob):
    i = index
    rate_list = []
    items = items # 1200,1,3
    items = items.squeeze()
    num_list = []
    temp = 0
    items_list = []
    prob_list = []
    p_list = []
    weights_list = []
    p = list(prob)
    load_items = creator.bin_create(items.clone(), i) # 装箱
    packer = Packer()
    # print(len(load_items))
    while len(load_items) > 0:
        if len(rate_list) == 0:
            # print(len(load_items))
            #carlist为list形式后续可以放长宽高不一样的车厢
            carlist[0].position = [0,0,0] #置空
            view_packing(carlist, load_items)
            break

def draw_all_car_and_save(carlist,items, index):
    i = index.unsqueeze(1)
    rate_list = []
    items = items
    items = items.squeeze()
    load_items = creator.bin_create_num(items.clone(), i) # 装箱 i:[N,1]
    packer = Packer()
    count = 0
    while len(load_items) > 0:
        if len(rate_list) == 0:
            count += 1
            carlist[0].position = [0,0,0] #置空
            load_item_temp = load_items
            view_packing(carlist, load_items,count)
           # _, _, _, _ = packer.packer_own(carlist, load_item_temp)
        else:
            count+=1
            carlist[0].position = [0,0,0] #置空
            load_item_temp = load_items

            view_packing(carlist, load_items,count)
            #_, _, _, _ = packer.packer_own(carlist, load_item_temp)

def draw_all_car_and_save_4ClassColor(carlist,items, index):
    i = index.unsqueeze(1)
    rate_list = []
    items = items
    items = items.squeeze()
    load_items = creator.bin_create_num(items.clone(), i) # 装箱 i:[N,1]
    packer = Packer()
    count = 0
    while len(load_items) > 0:
        if len(rate_list) == 0:
            count += 1
            carlist[0].position = [0,0,0] #置空
            load_item_temp = load_items
            view_packing_family(carlist, load_items,count)
           # _, _, _, _ = packer.packer_own(carlist, load_item_temp)
        else:
            count+=1
            carlist[0].position = [0,0,0] #置空
            load_item_temp = load_items

            view_packing_family(carlist, load_items,count)
            #_, _, _, _ = packer.packer_own(carlist, load_item_temp)


def draw_all_profit_and_save(carlist,items, index, prob):
    i = index
    rate_list = []
    items = items  # 1200,1,3
    items = items.squeeze()
    num_list = []
    temp = 0
    items_list = []
    prob_list = []
    p_list = []
    weights_list = []
    p = list(prob)
    load_items = creator.bin_create(items.clone(), i)  # 装箱
    packer = Packer()
    # print(len(load_items))
    while len(load_items) > 0:
        if len(rate_list) == 0:
            # print(len(load_items))
            # carlist为list形式后续可以放长宽高不一样的车厢
            carlist[0].position = [0, 0, 0]  # 置空
            rate, num, item_list, peritem_list = packer.packer_own(carlist, load_items)
            temp = num
            # print(num,len(load_items))
            rate_list.append(rate)
            num_list.append(num)
            items_list.append(item_list)
            weight_list = []
            for i in item_list:
                weight_list.append(i.weight)
            weights_list.append(weight_list)
            for k in range(num):
                prob_list.append(p[k])
            for k in range(num):
                p.pop(0)
        else:
            carlist[0].position = [0, 0, 0]  # 置空
            rate, num, item_list, peritem_list = packer.packer_own(carlist, load_items)
            rate_list.append(rate)
            num_list.append(num)
            items_list.append(item_list)
            weight_list = []
            for i in item_list:
                weight_list.append(i.weight)
            weights_list.append(weight_list)
            temp = num
            for k in range(num):
                prob_list.append(p[k])
            for k in range(num):
                p.pop(0)
        p_list.append(prob_list)
        prob_list = []
    try:
        avg_rate = torch.sum(torch.tensor(rate_list)) / len(rate_list)
    except:
        raise ZeroDivisionError
    plt_profit_list = []
    sum_list = []
    for i in items_list:
        #plt_profit_ = []
        sum = 0
        for j in i:
            #plt_profit_.append(j.weight)
            sum += int(j.weight.item())
        sum_list.append(sum)
        #plt_profit_.append(plt_profit_)

    plt.bar(range(len(sum_list)), sum_list)
    plt.show()
    #return sum_list

class ReplayBuffer:
    def __init__(self, capacity):
        self.buffer = collections.deque(maxlen=capacity)

    def add(self, state, action, reward, next_state, done):
        self.buffer.append((state, action, reward, next_state, done))

    def sample(self, batch_size):
        transitions = random.sample(self.buffer, batch_size)
        state, action, reward, next_state, done = zip(*transitions)
        return np.array(state), action, reward, np.array(next_state), done

    def size(self):
        return len(self.buffer)

class ReplayBuffer_MORL:
    def __init__(self, capacity):
        self.buffer_1 = collections.deque(maxlen=capacity)
        self.buffer_2 = collections.deque(maxlen=capacity)

    def add_1(self, pro, reward):
        self.buffer_1.append((1,pro, reward))
    def add_2(self, pro, reward):
        self.buffer_2.append((2,pro, reward))
    #从两个里main随机取样才对，而不是分别从两个里面取样
    def sample(self, batch_size):
        batch_size_1 = random.randint(1, batch_size-1)# batch_size里挑出一部分给buffer_1,另一部分名额给buffer_2
        batch_size_2 = batch_size - batch_size_1
        transitions_1 = random.sample(self.buffer_1, batch_size_1)
        class_1,pro_1, reward_1 = zip(*transitions_1)
        transitions_2 = random.sample(self.buffer_2, batch_size_2)
        class_2, pro_2, reward_2 = zip(*transitions_2)
        #将两部分结合成一个
        #print(type(state_2),type(action_2),type(reward_2)) 数据类型: python的元组：tuple
        class_all = class_1 + class_2
        pro = pro_1 + pro_2
        reward = reward_1 + reward_2
        return class_all, pro, reward

    def size(self):
        return len(self.buffer_1),len(self.buffer_2)
    def pop(self):
        self.buffer_1.pop()
        self.buffer_2.pop()

class ReplayBuffer_MORL_OneAgent:
    def __init__(self, capacity):
        self.buffer_1 = collections.deque(maxlen=capacity)

    def add_1(self, pro, reward):
        self.buffer_1.append((1, pro, reward))

    #从两个里main随机取样才对，而不是分别从两个里面取样
    def sample(self, batch_size):
        batch_size_1 = random.randint(1, batch_size-1)# batch_size里挑出一部分给buffer_1,另一部分名额给buffer_2
        transitions_1 = random.sample(self.buffer_1, batch_size_1)
        class_1,pro_1, reward_1 = zip(*transitions_1)
        #将两部分结合成一个
        #print(type(state_2),type(action_2),type(reward_2)) 数据类型: python的元组：tuple
        class_all = class_1
        pro = pro_1
        reward = reward_1
        return class_all, pro, reward

    def size(self):
        return len(self.buffer_1)
    def pop(self):
        self.buffer_1.pop()

#庄家法寻找帕累托最优解的方法,Q是所有解的集合

def Pareto_MakersAMethod(Q_tuple):#Q是Tensor
    #元组转成list
    Q = []
    for i in Q_tuple:
        Q.append(i.item())
    Q = np.array(Q)
    NDset = Q.max()

    return NDset
#
def plt_asu_gini(asu_list,gini_list,loss_list,NDSet_list):
    plt.figure(1)
    plt.subplot(221)
    episodes_list = list(range(len(asu_list)))
    plt.plot(episodes_list, asu_list)
    plt.subplot(222)
    episodes_list_ = list(range(len(gini_list)))
    plt.plot(episodes_list_, gini_list)
    plt.subplot(223)
    episodes_list__ = list(range(len(loss_list)))
    plt.plot(episodes_list__, loss_list)
    plt.subplot(224)
    episodes_list___ = list(range(len(NDSet_list)))
    plt.plot(episodes_list___, NDSet_list)

    plt.show()
def plt_(loss_list,asu_list,gini_list):
    plt.figure(1)
    plt.subplot(221)
    episodes_list = list(range(len(asu_list)))
    plt.plot(episodes_list, asu_list)
    plt.subplot(222)
    episodes_list_ = list(range(len(gini_list)))
    plt.plot(episodes_list_, gini_list)
    plt.subplot(223)
    episodes_list__ = list(range(len(loss_list)))
    plt.plot(episodes_list__, loss_list)
    plt.show()

def save_loss(loss_list):
    df = pd.DataFrame(data = loss_list, columns= ["loss"])
    df.to_csv("loss_6.csv")
def save_asu(loss_list):
    df = pd.DataFrame(data = loss_list, columns= ["asu"])
    df.to_csv("asu_6.csv")
def save_gini(loss_list):
    df = pd.DataFrame(data = loss_list, columns= ["gini"])
    df.to_csv("gini_6.csv")

def total_size(o, handlers={}, verbose=False):
    """ Returns the approximate memory footprint an object and all of its contents.

    Automa      tically finds the contents of the following builtin containers and
    their subclasses:  tuple, list, deque, dict, set and frozenset.
    To search other containers, add handlers to iterate over their contents:

        handlers = {SomeContainerClass: iter,
                    OtherContainerClass: OtherContainerClass.get_elements}

    """
    dict_handler = lambda d: chain.from_iterable(d.items())
    all_handlers = {tuple: iter,
                    list: iter,
                    deque: iter,
                    dict: dict_handler,
                    set: iter,
                    frozenset: iter,
                   }
    all_handlers.update(handlers)     # user handlers take precedence
    seen = set()                      # track which object id's have already been seen
    default_size = getsizeof(0)       # estimate sizeof object without __sizeof__

    def sizeof(o):
        if id(o) in seen:       # do not double count the same object
            return 0
        seen.add(id(o))
        s = getsizeof(o, default_size)

        if verbose:
            print(s, type(o), repr(o), file=stderr)

        for typ, handler in all_handlers.items():
            if isinstance(o, typ):
                s += sum(map(sizeof, handler(o)))
                break
        return s

    return sizeof(o)

def compute_advantage(gamma, lmbda, td_delta):
    td_delta = td_delta.detach().numpy()
    advantage_list = []
    advantage = 0.0
    for delta in td_delta[::-1]:
        advantage = gamma * lmbda * advantage + delta
        advantage_list.append(advantage)
    advantage_list.reverse() #!!!!!!!!!!!!!!!!!!!!!!!********************非常重要的点
    return torch.tensor(advantage_list, dtype=torch.float)

def to_csv(asu_list,gini_list,loss_list,NDSet_list,topsis_score,csv_name):
    saved = False
    asu_ = []
    gini_ = []
    loss_ = []
    NDSet_ = []
    topsis_ = []
    x_ = []
    for i in range(len(asu_list)):
        asu_.append(asu_list[i])
        gini_.append(gini_list[i])
        loss_.append(loss_list[i])
        NDSet_.append(NDSet_list[i])
        topsis_.append(topsis_score[i])
    data = np.vstack((asu_, gini_, loss_, NDSet_,topsis_)).T
    column_name = ['ASU', 'MSSE', 'Loss', 'Rewards', 'Topsis']
    df = pd.DataFrame(data, columns=column_name)
    df.to_csv(csv_name)

    return None
def to_csv_best_solver(asu_list,gini_list,loss_list,reward_list,topsis_score,csv_name,best_asu, best_msse, best_order):
    saved = False
    asu_ = []
    gini_ = []
    loss_ = []
    reward_ = []
    topsis_ = []
    x_ = []
    for i in range(len(asu_list)):
        asu_.append(asu_list[i])
        gini_.append(gini_list[i])
        loss_.append(loss_list[i])
        reward_.append(reward_list[i])
        topsis_.append(topsis_score[i])
    data = np.vstack((asu_, gini_, loss_, reward_, topsis_)).T
    column_name = ['ASU', 'MSSE', 'Loss', 'Rewards', 'Topsis']
    df = pd.DataFrame(data, columns=column_name)
    df.to_csv(csv_name+".csv")
    df = pd.DataFrame(data=[best_asu], columns = ['Best_ASU'])
    df.to_csv(csv_name+"Best_ASU"+".csv")
    df = pd.DataFrame(data=[best_msse], columns = ['Best_MSSE'])
    df.to_csv(csv_name+"Best_MSSE"+".csv")
    best_order_ = []
    for i in range(best_order.shape[0]):
        best_order_.append(best_order[i].item())
    df = pd.DataFrame(data=best_order_, columns=['Best_Order'])
    df.to_csv(csv_name+"Best_Order"+".csv")
    return None

#删除二维list的空list,a = [[],[2]] =>a = [[2]]
def del_empty_listItem_2dim(list):
    res_list = []
    for i in list:

        if len(i) != 0:
            res_list.append(i)
        else:
            continue
    return res_list






